
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#include <avr/io.h>
#include <avr/pgmspace.h>

#include <Arduino.h>

#include "FreeRTOS.h"
#include "task.h"

#include "debug.h"

//
// Define baud rate
//
//#define USART0_BAUD         115200ul
#define USART0_BAUD         9600ul
#define USART0_UBBR_VALUE   ((F_CPU/(USART0_BAUD<<4))-1)

//
// Define calibration factor for delayMS
//
#define CAL_FACTOR          (F_CPU/20000)

// ===================================================================
//   usart0_put_char
// ===================================================================
static int usart0_put_char (char data, FILE *stream)
{
  // Recursive function to prepend a carriage return before a new line
  if (data == '\n') {
    usart0_put_char('\r',stream);
  }
 
  // Wait if a byte is being transmitted
  while ((UCSR0A&(1<<UDRE0)) == 0) {
    ;
  }
 
  // Transmit data
  UDR0 = data;
 
  return 0;
}

//
// Create PRINTF Stream structure
//
static FILE printf_stream = FDEV_SETUP_STREAM(usart0_put_char, NULL, _FDEV_SETUP_WRITE);

// ===================================================================
//   delayMS
//
//   delay between led error flashes.
//
//   \param[in] millis milliseconds to delay
// ===================================================================
static void delayMS (uint32_t millis) 
{
  uint32_t iterations = millis * CAL_FACTOR;
  uint32_t i;
  for(i = 0; i < iterations; ++i) {
    asm volatile("nop\n\t");
  }
}

// ===================================================================
//   errorBlink
//
//   Blink error pattern
//
//   \param[in] n  number of short pulses
// ===================================================================
static void errorBlink (int n) 
{
  noInterrupts();
  pinMode(13, OUTPUT);
  for (;;) {
    int i;
    for (i = 0; i < n; i++) {
      digitalWrite(13, 1);
      delayMS(300);
      digitalWrite(13, 0);
      delayMS(300);
    }
    delayMS(2000);
  }
}

// ===================================================================
//   assertBlink
//
//   Blink 'n' short pulses every two seconds if configASSERT fails.
// ===================================================================
void assertBlink (int n) 
{
  errorBlink(n);
}

// ===================================================================
//   ASSERT
//
//   Print file and line when configASSERT is defied like this.
//   #define configASSERT( x ) if( ( x ) == 0 ) {ASSERT(__FILE__,__LINE__);}
// ===================================================================
void ASSERT (const char * file, int line) 
{
  PRINTF("%s.%d", file, line);
  noInterrupts();
  for (;;) {}
}

// ===================================================================
//   vApplicationIdleHook
// ===================================================================
void vApplicationIdleHook (void)
{
  //PRINTF("Idle Hook ... \n");
}

// ===================================================================
//   vApplicationMallocFailedHook
//
//   Blink two short pulses if malloc fails.
//
//   Will only be called if
//   configUSE_MALLOC_FAILED_HOOK is set to 1 in FreeRTOSConfig.h.  
//   It is a hook function that will get called if a call to pvPortMalloc() 
//   fails.
//   pvPortMalloc() is called internally by the kernel whenever a task, 
//   queue, timer or semaphore is created.  It is also called by various 
//   parts of the demo application.  
//   If heap_1.c or heap_2.c are used, then the size of the heap available 
//   to pvPortMalloc() is defined by configTOTAL_HEAP_SIZE in
//   FreeRTOSConfig.h, and the xPortGetFreeHeapSize() API function can 
//   be used to query the size of free heap space that remains (although 
//   it does not provide information on how the remaining heap might be 
//   fragmented)
// ===================================================================
void vApplicationMallocFailedHook (void) {
  errorBlink(2);
}

// ===================================================================
//   vApplicationStackOverflowHook
//
//   Blink three short pulses if stack overflow is detected.
//   Run time stack overflow checking is performed if
//   configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2.  
//   This hook function is called if a stack overflow is detected.
//   \param[in] pxTask Task handle
//   \param[in] pcTaskName Task name
// ===================================================================
void vApplicationStackOverflowHook (TaskHandle_t pxTask, char *pcTaskName) 
{
  //(void) pcTaskName;
  //(void) pxTask;
//  void assertMsg(const char* file, int line);
  //assertMsg(pcTaskName, 0);
  errorBlink(3);
}

// ===================================================================
//   vApplicationTaskSwitchHook
// ===================================================================
void vApplicationTaskSwitchHook (int task)
{
  //PRINTF("TaskSwitch %d \n", task);
}

// ===================================================================
//   vApplicationTraceMallocHook
// ===================================================================
void vApplicationTraceMallocHook (void * addr, size_t size)
{
  PRINTF ("Malloc: ");
  PRINTF (" 0x%08x, ", (uintptr_t)addr);
  PRINTF (" %d \n", (uint32_t)size);
}

// ===================================================================
//   vApplicationTaskDelayHook
// ===================================================================
void vApplicationTaskDelayHook (void)
{
  //PRINTF ("Task delay ...\n");
}

// ===================================================================
//   vApplicationTickHook
// ===================================================================
void vApplicationTickHook (void) 
{  
#if 0
  static int32_t tick = 0;
  PRINT("Tick: %d\n", tick);
  tick++;
#endif // 0
}

// ===================================================================
//   usart0_init
// ===================================================================
void usart0_init (void)
{
  // 
  // Communication parameters: 8-bit data, 1 Stop, No Parity
  // USART Receiver: on
  // USAR TYransmitter: on
  // USART Mode: Asynchronous
  // USART Baud: 9600
  // 

  //
  // Set baud rate
  //
  UBRR0H = (uint8_t) (USART0_UBBR_VALUE >> 8);
  UBRR0L = (uint8_t) USART0_UBBR_VALUE;

  //
  // Set frame format to 8 data bits, no parity, 1 stop bit
  //
  UCSR0C = (0<<USBS0)|(1<<UCSZ01)|(1<<UCSZ00);
 
  //
  // Enable receiver and transmitter
  //
  UCSR0B = (1<<RXEN0)|(1<<TXEN0);   

  //
  // discard any random input
  //
  //while (Serial.read() >= 0) {}

  //
  // Direct stdout stream to printf_stream
  //
  stdout = &printf_stream;

  PRINTF ("USART0 init at ");
  PRINTF ("%d", USART0_BAUD);
  PRINTF (" BAUD\n");

  return;
}


//}

